using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows.Forms;
using Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;

namespace word2html
{

    class Converter
    {
        private const string defaultCssStyleName = "word2html";
        private static string DOC_STYLE_SUFFIX = "_word2html";

        private static object missing = Missing.Value;


        public static void convert()
        {
            Document converted;

            Document original;

            original = Globals.ThisAddIn.Application.ActiveDocument;

            List<Tag> tags = new List<Tag>();

            bool inList = false;
            bool isUListPrev = false;
            Tag parent = null;

            try
            {
                converted = Globals.ThisAddIn.Application.Documents.Add(ref missing, ref missing, ref missing, ref missing);
                foreach (Paragraph par in original.Paragraphs)
                {
                    string wordStyleName = ((Style)par.get_Style()).NameLocal;

                    string text = par.Range.Text.Trim();

                    int fpos = par.Range.Start;
                    int lpos = par.Range.Text.Length;

                    List<Content> contents = new List<Content>();
                    Hyperlink hl = null;
                    foreach (Hyperlink hyperlink in par.Range.Hyperlinks)
                    {
                        int start = hyperlink.Range.Start;
                        if (fpos < start)
                        {
                            contents.Add(new Text(par.Range.Text.Substring(fpos, start-fpos)));
                        }
                        contents.Add(new TagA(defaultCssStyleName, hyperlink.Address, hyperlink.Range.Text));
                        hl = hyperlink;
                    }

                    if (hl != null)
                    {
                        int end = hl.Range.Start + hl.Range.Text.Length;
                        if (lpos > end)
                        {
                            contents.Add(new Text(par.Range.Text.Substring(end, lpos-end)));
                        }
                    }

                    if (inList && (
                                      (isUListPrev && wordStyleName.Equals("Numbered list" + DOC_STYLE_SUFFIX)
                                  ) ||
                                      (!isUListPrev && wordStyleName.Equals("Unnumbered list" + DOC_STYLE_SUFFIX))
                                  )
                        )
                    {
                        inList = false;
                        parent = null;
                    }
                    if (inList && (!wordStyleName.Equals("Unnumbered list" + DOC_STYLE_SUFFIX) && !wordStyleName.Equals("Numbered list" + DOC_STYLE_SUFFIX)))
                    {
                        inList = false;
                        parent = null;
                    }

                    if (wordStyleName.Equals("Paragraph" + DOC_STYLE_SUFFIX))
                    {
                        tags.Add(new Tag(Tag.TagNames.paragraph, defaultCssStyleName, contents));
                    }

                    if (wordStyleName.Equals("Heading 1" + DOC_STYLE_SUFFIX))
                    {
                        tags.Add(new Tag(Tag.TagNames.heading1, defaultCssStyleName, contents));
                    }

                    if (wordStyleName.Equals("Heading 2" + DOC_STYLE_SUFFIX))
                    {
                        tags.Add(new Tag(Tag.TagNames.heading2, defaultCssStyleName, contents));
                    }

                    if (wordStyleName.Equals("Heading 3" + DOC_STYLE_SUFFIX))
                    {
                        tags.Add(new Tag(Tag.TagNames.heading3, defaultCssStyleName, contents));
                    }
                    if (wordStyleName.Equals("Heading 4" + DOC_STYLE_SUFFIX))
                    {
                        tags.Add(new Tag(Tag.TagNames.heading4, defaultCssStyleName, contents));
                    }
                    if (wordStyleName.Equals("Heading 5" + DOC_STYLE_SUFFIX))
                    {
                        tags.Add(new Tag(Tag.TagNames.heading5, defaultCssStyleName, contents));
                    }
                    if (wordStyleName.Equals("Heading 6" + DOC_STYLE_SUFFIX))
                    {
                        tags.Add(new Tag(Tag.TagNames.heading6, defaultCssStyleName, contents));
                    }

                    if (wordStyleName.Equals("Code" + DOC_STYLE_SUFFIX))
                    {
                        text = text.Replace(" ", "&nbsp;");
                        tags.Add(new Tag(Tag.TagNames.code, defaultCssStyleName, text));
                    }

                    if (wordStyleName.Equals("Unnumbered list" + DOC_STYLE_SUFFIX))
                    {
                        if (!inList)
                        {
                            inList = true;
                            parent = new Tag(Tag.TagNames.uList, defaultCssStyleName);
                            parent.children.Add(new Tag(Tag.TagNames.listItem, defaultCssStyleName, contents));
                            tags.Add(parent);
                        }
                        else
                        {
                            parent.children.Add(new Tag(Tag.TagNames.listItem, defaultCssStyleName, contents));
                        }
                        isUListPrev = true;
                    }
                    if (wordStyleName.Equals("Numbered list" + DOC_STYLE_SUFFIX))
                    {
                        if (!inList)
                        {
                            inList = true;
                            parent = new Tag(Tag.TagNames.oList, defaultCssStyleName);
                            parent.children.Add(new Tag(Tag.TagNames.listItem, defaultCssStyleName, contents));
                            tags.Add(parent);
                        }
                        else
                        {
                            parent.children.Add(new Tag(Tag.TagNames.listItem, defaultCssStyleName, contents));
                        }

                        isUListPrev = false;
                    }
                }

                foreach (Tag tag in tags)
                {
                    converted.Paragraphs.Add(ref missing).Range.InsertAfter(tag.render());
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }

        }

        public interface Content
        {
            string render();
        }

        public class Text : Content
        {
            string text;

            virtual public string render()
            {
                return text;
            }

            public Text(string text)
            {
                this.text = text;
                this.text = this.text.Replace("<", "&lt;");
                this.text = this.text.Replace(">", "&gt;");
                this.text = this.text.Replace("\v", "<br/>");
            }
        }

        public class Constants
        {
            private static Dictionary<Tag.TagNames, String> tagNames;

            public static Dictionary<Tag.TagNames, string> getTagNamesDic()
            {
                if (tagNames == null)
                    constructTagNames();
                return tagNames;
            }

            static void constructTagNames()
            {
                tagNames = new Dictionary<Tag.TagNames, string>();
                tagNames.Add(Tag.TagNames.code, "code");
                tagNames.Add(Tag.TagNames.heading1, "h1");
                tagNames.Add(Tag.TagNames.heading2, "h2");
                tagNames.Add(Tag.TagNames.heading3, "h3");
                tagNames.Add(Tag.TagNames.heading4, "h4");
                tagNames.Add(Tag.TagNames.heading5, "h5");
                tagNames.Add(Tag.TagNames.heading6, "h6");
                tagNames.Add(Tag.TagNames.paragraph, "p");
                tagNames.Add(Tag.TagNames.uList, "ul");
                tagNames.Add(Tag.TagNames.oList, "ol");
                tagNames.Add(Tag.TagNames.listItem, "li");
                tagNames.Add(Tag.TagNames.a, "a");
            }
        }

        public class Tag : Content
        {
            public enum TagNames
            {
                heading1, heading2, heading3, heading4, heading5, heading6,
                paragraph, code, listItem, uList, oList,
                a
            }

            protected TagNames name;
            protected string styleClass;

            public List<Content> children;

            public virtual string render()
            {
                string buffer = "";
                buffer += "<" + Constants.getTagNamesDic()[name] + " class=" + "\"" + styleClass + "\"" + ">";
                foreach (Content con in children)
                {
                    buffer += con.render();
                }
                buffer += "</" + Constants.getTagNamesDic()[name] + ">";
                return buffer;
            }

            public Tag()
            {
                children = new List<Content>();
            }

            public Tag(TagNames tagName, string tagStyle)
                : this()
            {
                name = tagName;
                styleClass = tagStyle;
            }

            public Tag(TagNames tagName, string tagStyle, string content)
                : this(tagName, tagStyle)
            {
                children.Add(new Text(content));
            }

            public Tag(TagNames tagName, string tagStyle, Tag content)
                : this(tagName, tagStyle)
            {
                children.Add(content);
            }

            public Tag(TagNames tagName, string tagStyle, List<Content> contents)
                : this(tagName, tagStyle)
            {
                if (contents != null)
                    foreach (Content content in contents)
                    {
                        children.Add(content);
                    }
            }
        }

        public class TagA : Tag
        {

            protected string href;

            public TagA(string tagStyle, string href, String value)
            {
                name = TagNames.a;
                styleClass = tagStyle;

                this.href = href;

                children.Add(new Text(value));
            }

            public TagA(string tagStyle, string href, Tag value)
            {
                name = TagNames.a;
                styleClass = tagStyle;

                this.href = href;

                children.Add(value);
            }

            public override string render()
            {
                string buffer = "";
                buffer += "<" + Constants.getTagNamesDic()[name] + " class=" + "\"" + styleClass + "\"" + "href=\"" + href + "\">";
                foreach (Content con in children)
                {
                    buffer += con.render();
                }
                buffer += "</" + Constants.getTagNamesDic()[name] + ">";
                return buffer;
            }
        }
    }
}